#include "tcpcomm.hpp"
#include "spdlog/spdlog.h"
#include <cstring>
#include <cerrno>
#include <stdexcept>

TcpComm::TcpComm(const std::string& ip, unsigned int port)
    : ip_(ip), port_(port), is_server_(false) {}

TcpComm::TcpComm(unsigned int port)
    : port_(port), is_server_(true) {}

TcpComm::~TcpComm() {
    shutdown();
}

bool TcpComm::initialize() {
    if (!setupSocket()) {
        return false;
    }
    
    if (is_server_ && !acceptConnection()) {
        return false;
    }
    
    running_ = true;
    readThread_ = std::thread(&TcpComm::readThreadFunc, this);
    processThread_ = std::thread(&TcpComm::processReceivedData, this);
    
    return true;
}

bool TcpComm::setupSocket() {
    if (is_server_) {
        server_fd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (server_fd_ == -1) {
            spdlog::error("Socket creation failed: {}", strerror(errno));
            return false;
        }
        
        int opt = 1;
        if (setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
            spdlog::error("Setsockopt failed: {}", strerror(errno));
            close(server_fd_);
            return false;
        }
        
        struct sockaddr_in address;
        address.sin_family = AF_INET;
        address.sin_addr.s_addr = INADDR_ANY;
        address.sin_port = htons(port_);
        
        if (bind(server_fd_, (struct sockaddr*)&address, sizeof(address)) < 0) {
            spdlog::error("Bind failed: {}", strerror(errno));
            close(server_fd_);
            return false;
        }
        
        if (listen(server_fd_, 3) < 0) {
            spdlog::error("Listen failed: {}", strerror(errno));
            close(server_fd_);
            return false;
        }
        
        spdlog::info("TCP server listening on port {}", port_);
        return true;
    } else {
        client_fd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (client_fd_ == -1) {
            spdlog::error("Socket creation failed: {}", strerror(errno));
            return false;
        }
        
        struct sockaddr_in serv_addr;
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(port_);
        
        if (inet_pton(AF_INET, ip_.c_str(), &serv_addr.sin_addr) <= 0) {
            spdlog::error("Invalid address: {}", ip_);
            close(client_fd_);
            return false;
        }
        
        if (connect(client_fd_, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
            spdlog::error("Connection failed: {}", strerror(errno));
            close(client_fd_);
            return false;
        }
        
        int flags = fcntl(client_fd_, F_GETFL, 0);
        fcntl(client_fd_, F_SETFL, flags | O_NONBLOCK);
        
        spdlog::info("Connected to {}:{}", ip_, port_);
        return true;
    }
}

bool TcpComm::acceptConnection() {
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    
    spdlog::info("Waiting for client connection...");
    client_fd_ = accept(server_fd_, (struct sockaddr*)&address, (socklen_t*)&addrlen);
    
    if (client_fd_ < 0) {
        spdlog::error("Accept failed: {}", strerror(errno));
        return false;
    }
    
    // 设置非阻塞
    int flags = fcntl(client_fd_, F_GETFL, 0);
    fcntl(client_fd_, F_SETFL, flags | O_NONBLOCK);
    
    char ip_str[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &address.sin_addr, ip_str, INET_ADDRSTRLEN);
    spdlog::info("Client connected from {}:{}", ip_str, ntohs(address.sin_port));
    
    return true;
}

bool TcpComm::send(const std::vector<uint8_t>& data) {
    if (client_fd_ == -1 || !running_) return false;
    
    ssize_t sent = write(client_fd_, data.data(), data.size());
    if (sent < 0) {
        spdlog::error("Send failed: {}", strerror(errno));
        return false;
    }
    
    return sent == static_cast<ssize_t>(data.size());
}

std::vector<uint8_t> TcpComm::receive() {
    std::unique_lock<std::mutex> lock(dataMutex_);
    if (receiveQueue_.empty()) {
        return {};
    }
    
    auto data = receiveQueue_.front();
    receiveQueue_.pop();
    return data;
}

void TcpComm::registerCallback(DataReceivedCallback callback) {
    callback_ = std::move(callback);
}

std::string TcpComm::getName() const {
    if (is_server_) {
        return "TCP Server:" + std::to_string(port_);
    } else {
        return "TCP Client:" + ip_ + ":" + std::to_string(port_);
    }
}

bool TcpComm::isConnected() const {
    return client_fd_ != -1 && running_;
}

void TcpComm::shutdown() {
    running_ = false;
    
    if (readThread_.joinable()) {
        readThread_.join();     
    }
    
    if (processThread_.joinable()) {
        dataCond_.notify_all();
        processThread_.join();
    }
    
    if (client_fd_ != -1) {
        close(client_fd_);
        client_fd_ = -1;
    }
    
    if (server_fd_ != -1) {
        close(server_fd_);
        server_fd_ = -1;
    }
}

void TcpComm::readThreadFunc() {
    uint8_t buffer[BUFFER_SIZE];
    
    while (running_) {
        if (client_fd_ == -1) {
            if (is_server_) {
                acceptConnection();
            }
            std::this_thread::sleep_for(std::chrono::seconds(1));
            continue;
        }
        
        ssize_t bytesRead = read(client_fd_, buffer, sizeof(buffer));
        if (bytesRead > 0) {
            std::unique_lock<std::mutex> lock(dataMutex_);
            
            for (ssize_t i = 0; i < bytesRead; ++i) {
                ringBuffer_[head_] = buffer[i];
                head_ = (head_ + 1) % BUFFER_SIZE;
                
                if (head_ == tail_) {
                    tail_ = (tail_ + 1) % BUFFER_SIZE;
                }
            }
            
            dataCond_.notify_one();
        } else if (bytesRead == 0) {
            // 连接关闭
            spdlog::warn("Connection closed by peer");
            close(client_fd_);
            client_fd_ = -1;
        } else if (bytesRead == -1) {
            if (errno != EAGAIN && errno != EWOULDBLOCK) {
                spdlog::error("Read error: {}", strerror(errno));
                running_ = false;
            }
        }
        
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

void TcpComm::processReceivedData() {
    while (running_) {
        std::unique_lock<std::mutex> lock(dataMutex_);
        dataCond_.wait(lock, [this] { return !running_ || head_ != tail_; });
        
        if (!running_) break;
        
        std::vector<uint8_t> data;
        while (tail_ != head_) {
            data.push_back(ringBuffer_[tail_]);
            tail_ = (tail_ + 1) % BUFFER_SIZE;
        }
        
        if (!data.empty()) {
            receiveQueue_.push(data);
            
            if (callback_) {
                lock.unlock();
                callback_(data);
                lock.lock();
            }
        }
    }
}